import conf from "../core/conf";
import { LogMgr } from "../core/LogMgr";

type constructorView<T = IView> = new (...args: any[]) => T;

//UI约束
export interface IView extends Laya.Sprite {

    /**ui实例调用一次 */
    onInit?(): void;

    /**ui打开调用一次 */
    onShow?(...args: any[]): void;

    /**ui打开时每秒调用 */
    onUpdateTime?(): void;

    /**ui关闭调用一次 */
    onClear?(reason: string): void;

    /**ui销毁调用一次 */
    onDispose?(): void;

    /**支付回调 */
    onPayCall?(result: any): void;

}

//UI界面打开信息
export class ViewInfo {
    /**页面唯一标识 */
    id: number = -1;

    /**ui实例 */
    ui: IView;

    /**ui界面类型 */
    uiView: constructorView;

    /**页面组 */
    group: number = 0;

    /**打开时间 */
    openTime: number = -1;

    /**关闭时间 */
    closeTime: number = -1;

    /**是否常驻 */
    permanent: boolean = false;

    /**销毁时间 */
    disposeTime: number = -1;

    /**销毁计数，命中三次就清理ui对应的资源 */
    disposeCount: number = 0;
}


//UI打开参数
export interface UIOption {

    /**携带参数 */
    data: any[];

    /**ui名称 */
    name: string;

    /**页面组 */
    group: number;

    /**层级 */
    layer: number;

    /**父节点 */
    parent: Laya.Sprite;

    /**为true时如果已经打开会先关闭当前实例，再重新打开 */
    overwrite: boolean;

    /**常驻界面，不会被销毁 */
    permanent: boolean;

    /**是否是预加载,等加载完成再打开界面默认true */
    perloader: boolean;
}


/**
 * UI管理脚本，管理所有使用CreateUI打开的UI的生命周期,用此脚本创建的ui会自动销毁并清理UI带的资源
 */
export class UIManage {


    //当前UI信息
    public static uis: Map<number, ViewInfo> = new Map<number, ViewInfo>();

    //UI默认缓存时间 
    public static defaultUIdisposeTime: number = 15000;

    //打开ui数量
    private static uiCount: number = 0;


    /**
     * 打开ui
     * @param ui  
     * @param option 
     * @returns void
     */
    public static async CreateUI(ui: constructorView, option?: Partial<UIOption>) {

        if (!ui) {
            return;
        }

        if (typeof ui["uiView"] === "string") {
            ui["uiView"] = Laya.loader.getRes(ui["uiView"]);
        }

        if (!ui["uiView"]) {
            return;
        }


        option = option || {};

        //ui已经打开
        if (this.hasOpen(ui)) {
            let overwrite = !!option.overwrite;

            //不能重复打开
            if (!overwrite) {
                this.logUI("ui already open", ui.name);
                return;
            }

            this.ClearUI(ui);
        }

        let info = this.getUIinfo(ui);
        if (info.openTime > 0) {
            return;
        }

        let view = info.ui;

        info.closeTime = -1;
        info.disposeCount = 0;
        info.disposeTime = -1;

        info.openTime = Date.now();
        info.permanent = !!option.permanent;
        info.group = option.group || 0;

        if (option.perloader !== false) {
            await this.LoadUI(ui);
        }

        if (!view || view.destroyed) {
            view = info.ui = new (this.classView(ui))(option);
            this.logUI("create new ui", ui.name);
        }

        if (option.layer) {
            view.zOrder = option.layer;
        }

        (<any>view).uuid = info.id;
        view.name = option.name || ui.name;

        this.logUI("open ui", ui.name);
        (view as any).__open(option);

        if (option.parent) {
            option.parent.addChild(view);
        } else {
            conf.addChild(info.ui);
        }
    }


    //关闭UI
    public static ClearUI(ui: constructorView | number) {

        let id = typeof ui == "number" ? ui : this.getGID(ui, false);
        let info = this.uis.get(id);
        if (!info || !info.ui || info.openTime < 0 || info.closeTime > 0) {
            return;
        }

        this.logUI("close ui", info.ui.name);

        info.openTime = -1;
        info.closeTime = Date.now();
        (info.ui as any).__close();
    }

    //关闭一组UI
    public static ClearuiByGroup(group: number) {
        this.uis.forEach((v, k) => {
            if (v.group === group) {
                this.ClearUI(v.id);
            }
        });
    }

    /**关闭所有UI */
    public static ClearAll(clear: boolean = false) {

        this.uis.forEach(v => this.ClearUI(v.id));
        clear && this.uis.clear();
    }


    /**是否已经打开 */
    public static hasOpen(ui: constructorView | number) {

        let id = typeof ui == "number" ? ui : this.getGID(ui, false);
        let view = this.uis.get(id);

        if (view && view.ui && !view.ui.destroyed && view.openTime > 0) {
            return true;
        }

        return false;
    }


    //获取打开的UI
    public static getOpenUI(ui: constructorView) {
        let info = this.uis.get(this.getGID(ui, false));

        if (info && info.openTime > 0) {
            return info.ui;
        }

        return null;
    }

    public static LoadUI(ui: any, callback?: Laya.Handler) {

        return new Promise<void>((resolve, reject) => {
            let done = () => {
                resolve();
                callback && callback.run();
                callback = null;
            }

            if (!ui) {
                done();
                return;
            }

            if (typeof ui.uiView == "string") {
                ui.uiView = Laya.loader.getRes(ui.uiView);
            }

            if (!ui.uiView || ui.uiView.loadList.length <= 0) {
                done();
            }

            Laya.loader.load(ui.uiView.loadList, Laya.Handler.create(null, () => {
                done();
            }));
        })
    }


    public static updateTime() {

        this.uis.forEach((v, k) => {

            //UI已经打开
            if (this.hasOpen(v.id)) {
                v.ui.onUpdateTime && v.ui.onUpdateTime();
                return;
            }

            //常驻页面,不处理
            if (v.permanent) {
                return;
            }

            let currtm = Date.now();

            //一段时间没打开自动动销毁UI
            if (v.closeTime > 0 && currtm - v.closeTime > this.defaultUIdisposeTime && v.ui && !v.ui.destroyed) {

                this.logUI("dispose ui", v.ui?.name);
                (v.ui as any).__dispose();
                v.disposeTime = currtm;

                v.openTime = v.closeTime = -1;
                v.ui = null;
            }

            if (v.disposeTime > 0 && currtm - v.disposeTime > this.defaultUIdisposeTime) {
                v.disposeTime = currtm;
                v.disposeCount++;
            }

            //连续3次命中就清理资源
            if (v.disposeCount > 2) {

                v.disposeCount = 0;
                v.disposeTime = -1;

                this.clearResByUI(v.uiView);
            }

        })
    }

    public static onPayCall(result: any) {

        this.uis.forEach((v, k) => {

            //不存在UI实例不用处理
            if (!v.ui || v.ui.destroyed) {
                return;
            }

            v.ui && v.ui.onPayCall && v.ui.onPayCall(result);

        })
    }


    /**
     * 清理UI资源，调用尽量频率低一点
     * @param ui ui
     * @param no_arr 不需要清理的资源 
     * @returns 
     */
    private static clearResByUI(ui: any) {

        if (!ui || !ui.uiView) return;

        let arr = ui.uiView.loadList || [];
        if (!arr || arr.length <= 0) return;

        let clear_arr = [];
        for (let elm of arr) {

            if (elm.indexOf("cdn/") < 0) continue;

            let res = Laya.loader.getRes(elm);
            if (!res || res._referenceCount > 0) continue;

            Laya.loader.clearRes(elm);
            clear_arr.push(elm);
        }

        this.logUI("clear ui res", conf.release ? clear_arr.length : clear_arr);
    }


    private static getUIinfo(ui: constructorView): ViewInfo {
        let id = this.getGID(ui);
        let info = this.uis.get(id);
        if (!info) {
            info = new ViewInfo();
            info.id = id;
            info.uiView = ui;
            this.uis.set(id, info);
        }

        return info;
    }



    private static classView(baseUI: constructorView) {

        return class extends baseUI {

            private __data: any[];

            private uuid: number;
            private reason: string;

            constructor(option: Partial<UIOption>) {
                super();
                this.__data = option.data || [];
            }

            onAwake(): void {
                this.onInit && this.onInit();
                this.onShow && this.onShow(...this.__data);
                this.onUpdateTime && this.onUpdateTime();
            }

            __open(option: Partial<UIOption>) {
                //携带参数
                this.__data = option.data || [];

                if ((this as any)._getBit(Laya.Const.AWAKED)) {
                    this.onShow && this.onShow(...this.__data);
                    this.onUpdateTime && this.onUpdateTime();
                }
            }

            __close() {
                this.onClear && this.onClear(this.reason);
                this.removeSelf();
            }

            __dispose() {

                this.onDispose && this.onDispose();
                this.destroy();
            }

            close(reason?: string) {
                this.reason = reason;
                UIManage.ClearUI(this.uuid);
            }
        }

    }


    private static logUI(...str: any[]) {
        LogMgr.log("[UI]", ...str);
    }


    private static getID() {
        // let id = ++this.uiCount;
        // this.logUI("get id", id);
        // return id;
        return ++this.uiCount;

    };

    public static getGID(c: constructorView, create: boolean = true): number {
        const v = Object.getOwnPropertyDescriptor(c, "$__GID");

        if (v != null && v.value != null) {
            return v.value;
        }

        if (!create) {
            return -1;
        }

        let num = this.getID();
        Object.defineProperty(c, "$__GID", {
            enumerable: true,
            writable: false,
            configurable: false,
            value: num
        })

        return num;
    }

}